/*
 * Copyright 2011-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0.
 * See `LICENSE` in the project root for license information.
 */

package me.ijleex.mgmt.codegen.service.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;
import org.apache.velocity.exception.VelocityException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.google.common.base.Strings;

import me.ijleex.mgmt.codegen.boot.autoconfigure.CodeGenProperties;
import me.ijleex.mgmt.codegen.entity.ColumnInfo;
import me.ijleex.mgmt.codegen.entity.TableInfo;
import me.ijleex.mgmt.codegen.mapper.CodeGenMapper;
import me.ijleex.mgmt.codegen.service.ICodeGenService;
import me.ijleex.mgmt.codegen.util.CodeGenUtil;
import me.ijleex.mgmt.framework.constant.Constants;

/**
 * 代码生成 服务层处理
 *
 * @author liym
 * @since 2019-02-26 11:17 新建
 */
@Service
public class CodeGenServiceImpl implements ICodeGenService {

    /**
     * 用于渲染模板的编码方式
     */
    private static final String UTF_8 = StandardCharsets.UTF_8.name();

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final CodeGenMapper codeGenMapper;
    private final CodeGenProperties properties;

    public CodeGenServiceImpl(CodeGenMapper codeGenMapper, CodeGenProperties properties) {
        this.codeGenMapper = codeGenMapper;
        this.properties = properties;
    }

    @Override
    public List<TableInfo> getTableList(TableInfo tableInfo) {
        if (tableInfo == null) {
            return Collections.emptyList();
        }
        return this.codeGenMapper.getTableList(tableInfo);
    }

    @Override
    public List<ColumnInfo> getTableColumns(String tableName) {
        if (Strings.isNullOrEmpty(tableName)) {
            return Collections.emptyList();
        }
        return this.codeGenMapper.getColumnsByTableName(tableName);
    }

    @Override
    public byte[] generateCode(String tableName, String tablePrefix, String moduleName) {
        if (Strings.isNullOrEmpty(tableName)) {
            return new byte[0];
        }
        if (Strings.isNullOrEmpty(moduleName)) {
            moduleName = "codegen";
        }

        TableInfo table = this.codeGenMapper.getTableByName(tableName);
        if (table == null) {
            this.logger.error("generateCode error: table[{}] not exists.", tableName);
            return new byte[0];
        }
        List<ColumnInfo> columns = this.codeGenMapper.getColumnsByTableName(tableName);
        // if (columns.isEmpty()) {}

        CodeGenProperties.CodeGen codeGen = this.properties.getCodeGen();
        String basicPackage = codeGen.getBasicPackage();
        Map<String, String> templates = codeGen.getTemplates();
        System.out.println(basicPackage + " ---- " + templates);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try (ZipOutputStream zip = new ZipOutputStream(out)) {
            // 生成代码
            generateCode(table, columns, tablePrefix, basicPackage, moduleName, zip);
        } catch (IOException e) {
            this.logger.error("generateCode error", e);
        }
        return out.toByteArray();
    }

    /**
     * 根据 表、表的列信息 生成代码
     *
     * @param table 表信息
     * @param columns 表的列信息
     * @param basicPackage 项目/模块的基本包名，如 me.ijleex.mgmt
     * @param moduleName 模块的名称，如 codegen
     * @param zip 生成的代码的压缩包
     * @see org.apache.velocity.app.Velocity#getTemplate(String, String)
     * @see org.apache.velocity.Template#merge(Context, Writer)
     * @since 2019-02-26 11:39 新建方法
     * @since 2019-03-01 17:58 添加两个参数：basicPackage、moduleName
     */
    private void generateCode(TableInfo table, List<ColumnInfo> columns, String tablePrefix, String basicPackage,
            String moduleName, ZipOutputStream zip) {
        String className = CodeGenUtil.tableNameToClassName(table.getTableName(), tablePrefix);
        table.setClassName(className);
        table.setClassVarName(CodeGenUtil.toLowerCamelCase(className));

        // 设置列信息
        table.setColumns(CodeGenUtil.transferColumns(columns));
        // 设置主键
        table.setPrimaryKey(CodeGenUtil.detectPrimaryKey(columns));

        VelocityContext context = CodeGenUtil.getVelocityContext(table, basicPackage, moduleName, "liym");

        List<String> templates = CodeGenUtil.getTemplates();
        for (String templateName : templates) {
            // for-each try-with-resources
            try (StringWriter writer = new StringWriter()) {
                // 渲染模板
                Velocity.mergeTemplate(templateName, UTF_8, context, writer);

                // 添加文件到 zip
                String fileName = CodeGenUtil.getFileName(templateName, table, moduleName);
                zip.putNextEntry(new ZipEntry(fileName));
                // 添加文件内容到文件中
                String content = writer.toString();
                zip.write(content.getBytes(StandardCharsets.UTF_8));
                zip.closeEntry();
            } catch (IOException | VelocityException e) {
                // 渲染模板失败
                this.logger.error("generateCode Velocity.mergeTemplate table[" + table.getTableName() + "] error", e);
            }
        }
    }

    @Override
    public byte[] generateCodeBatch(String tableName, String tablePrefix, String moduleName) {
        if (Strings.isNullOrEmpty(tableName)) {
            return new byte[0];
        }
        String[] tableNames = tableName.split(Constants.DELIMITER);
        if (tableNames.length == 0) {
            return new byte[0];
        }
        if (Strings.isNullOrEmpty(moduleName)) {
            moduleName = "codegen";
        }

        return new byte[0];
    }

}
